#ifndef qc_obj
#include "bsp.h"
#include "pincfg.h"
#include "mbapp.h"
#include "com_def.h"

/*****************************************************************************
 * @brief 虚拟接口
 *****************************************************************************/
#define qc_rx_enable()
#define qc_tx_enable()
#define RS485DE_CTR_IN_TIMER 0

qc_obj_type qc_obj;
#define qc_port USART2
#define qc_uart_irqn USART2_IRQn

#define DMA_COM_RX DMA_U01_RX
#define DMA_COM_TX DMA_U01_TX
#define com_tx_dma_irq qc01_tx_dma_irq

static void qc_cb_set(void) {}
static void qc_port_hal_cfg(void) {}
#endif

/*****************************************************************************
 * @brief 预处理
 *****************************************************************************/
#ifdef DMA_COM_RX
#define RX_DMA_EN 1
#else
#define RX_DMA_EN 0
#endif

#ifdef DMA_COM_TX
#define TX_DMA_EN 1
#else
#define TX_DMA_EN 0
#endif
/*****************************************************************************
 * @brief 基本接口
 *****************************************************************************/
static void qc_cb_set(void);
static void qc_tx1_rx0_enable(uint8_t mode);
static void qc_port_Init(uu8 mbMode, uint32_t ulBaudRate, mb_parity_type eParity);
static void qc_port_send(void);
static void qc_port_hal_cfg(void);
static void (*qc_byte_send)(uint8_t dat);

#if RX_DMA_EN > 0
static void com_dma_rx_init(void);
static void com_dma_rx_restart(void);
#endif

#if TX_DMA_EN > 0
static void com_dma_tx_init(void);
static void com_dma_tx_send(void);
static void com_tx_dma_irq(void);
#endif

/*****************************************************************************
 * @brief UART OPT
 *****************************************************************************/
// 数据收发
__STATIC_INLINE void uart_send_data(UU08 dat) { qc_port->DATA = dat; }
__STATIC_INLINE UU08 uart_receive_data(void) { return qc_port->DATA; }

// 缓冲状态
__STATIC_INLINE UU32 uart_rx_not_empty_st(void) { return ((qc_port->STS_B.RXBNEFLG) != 0); } // rx not empty
__STATIC_INLINE UU32 uart_tx_not_full_st(void) { return (0 != (qc_port->STS_B.TXBEFLG)); }	// tx not full
__STATIC_INLINE UU32 uart_tx_comp_st(void) { return (qc_port->STS_B.TXCFLG); }
__STATIC_INLINE void uart_tx_tc_clear(void) { qc_port->STS_B.TXCFLG = 0; }

// 接收控制
__STATIC_INLINE void uart_rx_enable(void) { qc_port->CTRL1_B.RXEN = 1; }
__STATIC_INLINE void uart_rx_int_enable(void) { qc_port->CTRL1_B.RXBNEIEN = 1; }
__STATIC_INLINE void uart_rx_dma_enable(void) { qc_port->CTRL3_B.DMARXEN = 1; /* 开启RX DMA */ }
__STATIC_INLINE void uart_rx_int_disable(void)
{
	qc_port->CTRL1_B.RXEN = 0;
	qc_port->CTRL1_B.RXBNEIEN = 0;
	qc_port->CTRL3_B.DMARXEN = 0;
}
__STATIC_INLINE void uart_rx_data_clear(void)
{
	volatile uint8_t u8Temp;

	u8Temp = qc_port->DATA;
	while ((qc_port->STS & (USART_FLAG_RXBNE | USART_FLAG_OVRE)) != 0)
	{
		u8Temp = qc_port->DATA;
		__DSB();
	}
}

// 发送控制
__STATIC_INLINE void uart_tx_enable(void) { qc_port->CTRL1_B.TXEN = 1; }
__STATIC_INLINE void uart_tx_tde_enable(void)
{
	qc_port->CTRL1_B.TXCIEN = 0;
	qc_port->CTRL1_B.TXBEIEN = 1;
}
__STATIC_INLINE void uart_tx_dma_enable(void)
{
	qc_port->CTRL1_B.TXCIEN = 0;
	qc_port->CTRL1_B.TXBEIEN = 0;
	qc_port->CTRL3_B.DMATXEN = 1;
}
__STATIC_INLINE void uart_tx_tc_enable(void)
{
	qc_port->CTRL1_B.TXBEIEN = 0;	
	qc_port->CTRL1_B.TXCIEN = 1;
}
__STATIC_INLINE void uart_tx_int_disable(void)
{
	qc_port->CTRL1_B.TXCIEN = 0;
	qc_port->CTRL1_B.TXBEIEN = 0;
	qc_port->CTRL3_B.DMATXEN = 0;
}

// 停止位
__STATIC_INLINE void uart_stop_1bits_set(void) {qc_port->CTRL1_B.UEN=0; qc_port->CTRL2_B.STOPCFG = USART_STOP_BIT_1; qc_port->CTRL1_B.UEN=1; }
__STATIC_INLINE void uart_stop_2bits_set(void) {qc_port->CTRL1_B.UEN=0;  qc_port->CTRL2_B.STOPCFG = USART_STOP_BIT_2; qc_port->CTRL1_B.UEN=1; }

// 错误标志及清除
#define UART_RX_ERR_FLAG (USART_FLAG_PE | USART_FLAG_FE)
__STATIC_INLINE void uart_rx_error_clear(void) {}

/*****************************************************************************
 * @brief mb_byte_send
 * @param   dat
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_rtu_byte_send(uint8_t dat)
{
	uart_send_data(dat);
}

/*****************************************************************************
 * @brief UART interrupt routine.
 * @param   none
 * @return  none
 *****************************************************************************/
void qc_isr(void)
{
	volatile uint32_t IntSt;
	volatile uint8_t Data;

	IntSt = qc_port->STS;

	// 接收中断
	if ((IntSt & USART_FLAG_RXBNE) && (qc_port->CTRL1_B.RXBNEIEN))
	{
		Data = uart_receive_data();

		if ((IntSt & UART_RX_ERR_FLAG) != 0)
		{
			if (qc_obj.rx_cnt >= 1)
			{
				qc_obj.err_hal = 1;
			}
			uart_rx_error_clear();
		}

		qc_data_rcv(&qc_obj, Data);
	}
	// 发送完成中断
	else if ((IntSt & USART_FLAG_TXC) && qc_port->CTRL1_B.TXCIEN)
	{
		qc_send_end(&qc_obj);
		qc_tx1_rx0_enable(0);
		uart_tx_tc_clear();
	}
	// 发送空中断
	else if ((IntSt & USART_FLAG_TXBE) && (qc_port->CTRL1_B.TXBEIEN))
	{
		if (qc_data_send(&qc_obj, qc_rtu_byte_send, 1))
		{
			uart_tx_tc_enable();
		}
	}
	else
	{
	}
}

/*****************************************************************************
 * @brief   	com modle init .
 * @param   none
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_port_Init(uu8 qcMode, uint32_t ulBaudRate, mb_parity_type eParity)
{
	USART_Config_T cfg;

	USART_ConfigStructInit(&cfg);
	qc_port_hal_cfg();

// 关闭 DMA
#if RX_DMA_EN > 0
	dma_nvic_set(&DMA_COM_RX, 0, 0);
	dma_stream_deinit(&DMA_COM_RX);
	dma_stream_disable(&DMA_COM_RX);
#endif

#if TX_DMA_EN > 0
	dma_nvic_set(&DMA_COM_TX, 0, 0);
	dma_stream_deinit(&DMA_COM_TX);
	dma_stream_disable(&DMA_COM_TX);
#endif

	qc_cb_set();
	qc_obj.master_mode = qcMode;
	qc_obj.dat_send = qc_port_send;

	qc_byte_send = qc_rtu_byte_send;

	// rtu mode
	if (MB_PAR_NONE == eParity)
	{
		cfg.wordLength = USART_WORD_LEN_8B;
		cfg.stopBits = USART_STOP_BIT_2;
		cfg.parity = USART_PARITY_NONE;
	}
	else if (MB_PAR_NONE_1S == eParity)
	{
		cfg.wordLength = USART_WORD_LEN_8B;
		cfg.stopBits = USART_STOP_BIT_1;
		cfg.parity = USART_PARITY_NONE;
	}
	else if (MB_PAR_ODD == eParity)
	{
		cfg.wordLength = USART_WORD_LEN_9B;
		cfg.stopBits = USART_STOP_BIT_1;
		cfg.parity = USART_PARITY_ODD;
	}
	else
	{
		cfg.wordLength = USART_WORD_LEN_9B;
		cfg.stopBits = USART_STOP_BIT_1;
		cfg.parity = USART_PARITY_EVEN;
	}

	cfg.baudRate = ulBaudRate;
	cfg.hardwareFlow = USART_HARDWARE_FLOW_NONE;
	cfg.mode = USART_MODE_TX_RX;
	
	// rtu timer
	if (ulBaudRate > 19200)
	{
		qc_obj.tim_sv = 3;
	}
	else
	{
		qc_obj.tim_sv = (com_timer_cal(ulBaudRate, (35 * 11 + 9) / 10, 300, 12000) + 99) / 100;
	}

	USART_Config(qc_port, &cfg);
	USART_Enable(qc_port);

	nvic_irq_set(qc_uart_irqn, 0x01, 1);

#if RX_DMA_EN > 0
	com_dma_rx_init();
#endif

#if TX_DMA_EN > 0
	com_dma_tx_init();
#endif

	qc_tx1_rx0_enable(0);

	uart_tx_enable();
}

#if RX_DMA_EN > 0
/*****************************************************************************
 * @brief   dma 接收初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_init(void)
{
	dma_stream_cfg_type cfg;

	// 配置参数
	cfg.cycle_mode = 0;
	cfg.mem_to_periph = 0;
	cfg.prio = DMA_PRIORITY_MEDIUM;

	cfg.mem = qc_obj.buff;
	cfg.periph = &qc_port->DATA;
	cfg.data_len = sizeof(qc_obj.buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;

	// 配置
	dma_stream_config(&DMA_COM_RX, &cfg);
	dma_stream_enable(&DMA_COM_RX);
}

/*****************************************************************************
 * @brief   dma 接收重置
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_restart(void)
{
	intx_alloc();

	intx_disable();

	// 重置DMA计数器
	uart_rx_dma_enable();
	dma_stream_disable(&DMA_COM_RX); // 手动停止会产生TC标志

	dma_stream_set_mem_addr(&DMA_COM_RX, (uu32)qc_obj.buff);
	dma_stream_set_data_len(&DMA_COM_RX, sizeof(qc_obj.buff));

	dma_stream_enable(&DMA_COM_RX);

	intx_enable();
}
#endif

#if TX_DMA_EN > 0
/*****************************************************************************
 * @brief   dma 发送初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_init(void)
{
	dma_stream_cfg_type cfg;

	// 配置参数
	cfg.cycle_mode = 0;
	cfg.mem_to_periph = 1;
	cfg.prio = DMA_PRIORITY_MEDIUM;

	cfg.mem = qc_obj.buff;
	cfg.periph = &qc_port->DATA;
	cfg.data_len = sizeof(qc_obj.buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;

	// 配置
	dma_stream_config(&DMA_COM_TX, &cfg);
	dma_stream_complete_isr_enable(&DMA_COM_TX);
	dma_nvic_set(&DMA_COM_TX, 1, com_tx_dma_irq);
}

/*****************************************************************************
 * @brief   dma 发送中断
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_tx_dma_irq(void)
{
	// 清除标志 并关闭通道
	dma_stream_disable(&DMA_COM_TX);
	dma_comp_flag_clr(&DMA_COM_TX);

	// 开启发送结束中断
	qc_obj.tx_cnt = qc_obj.tx_size;
	uart_tx_tc_enable();
}

/*****************************************************************************
 * @brief   dma tx check: 该函数需要重入，所有操作在临界段内执行
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_send(void)
{
	// 发送状态
	qc_obj.hal_ctr.st_dir_ctr_req = 0;

	// 开启DMA传输
	uart_tx_tc_clear(); // 必须在开始发送时清除该状态，DMA结束不可清除，否则由于中断抢占和延时可能错过TC中断
	uart_tx_dma_enable();

	dma_stream_set_mem_addr(&DMA_COM_TX, (uu32)qc_obj.buff);
	dma_stream_set_data_len(&DMA_COM_TX, qc_obj.tx_size);
	dma_stream_enable(&DMA_COM_TX);

	qc_obj.hal_ctr.timeout_cnt = 0;
	qc_obj.hal_ctr.timeout_set = (qc_obj.tim_sv * qc_obj.tx_size / 2);
}
#endif

/*****************************************************************************
 * @brief   timer handle.
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_timer_run(void)
{
#if RX_DMA_EN > 0
	uint16_t rx_site;
#endif
	qc_timer_handle(&qc_obj);

#if RX_DMA_EN > 0
	// 对于DMA接收，状态变为IDLE时重置接收器
	if (qc_obj.hal_ctr.com_st_old != qc_obj.com_st)
	{
		if (qc_obj.com_st == QC_IDLE)
		{
			com_dma_rx_restart();
			qc_obj.rx_cnt = 0;
		}
		qc_obj.hal_ctr.com_st_old = qc_obj.com_st;
	}

	// 根据DMA计数器判定接收状态
	rx_site = dma_stream_get_data_len(&DMA_COM_RX);
	rx_site = sizeof(qc_obj.buff) - rx_site;
	if (rx_site && (rx_site != qc_obj.rx_cnt))
	{
		// 仅空闲、接收、等待状态允许接收数据
		if (QC_RX_RCV == qc_obj.com_st)
		{
			qc_obj.tim_cnt = 0;
			qc_obj.tout_cnt = 0;
			qc_obj.rx_cnt = rx_site;
		}
		else if ((QC_IDLE == qc_obj.com_st) || (QC_RX_WAIT == qc_obj.com_st))
		{
			qc_obj.com_st = QC_RX_RCV;

			qc_obj.tim_cnt = 0;
			qc_obj.tout_cnt = 0;
			qc_obj.rx_cnt = rx_site;
		}
	}

#endif

// DMA 发送超时强制结束
#if TX_DMA_EN > 0
	if (is_dma_stream_enable(&DMA_COM_TX))
	{
		qc_obj.hal_ctr.timeout_cnt++;
		if (qc_obj.hal_ctr.timeout_cnt > qc_obj.hal_ctr.timeout_set)
		{
			com_tx_dma_irq();
			qc_tx1_rx0_enable(0);
		}
	}
#endif
}

/*****************************************************************************
 * @brief   Uart En or Dis.
 * @param   mode
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/

static void qc_tx1_rx0_enable(uint8_t mode)
{
	if (0 == mode)
	{
		qc_rx_enable();

		uart_stop_1bits_set();

		uart_tx_int_disable();

		uart_rx_enable();

		uart_rx_data_clear();

#if RX_DMA_EN > 0
		com_dma_rx_restart();
#else
		uart_rx_int_enable();
#endif
	}
	else
	{
		qc_tx_enable();

		uart_stop_2bits_set();

		uart_rx_int_disable();

#if TX_DMA_EN > 0
		com_dma_tx_send();
#else
		uart_tx_tde_enable();
#endif
	}
}

/*****************************************************************************
 *
 * @brief   Send data.
 *
 * @param   none
 *
 * @return  none
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void qc_port_send(void)
{
	qc_tx1_rx0_enable(1);
}

/*****************************************************************************
 * @brief   sim_mbport_keep
 * @param   none
 * @return  none
 *****************************************************************************/
#ifndef qc_obj
void qc_sim_mbport_keep(void)
{
	qc_port_Init(0, 9600, MB_PAR_NONE);
}
#endif